-- ============================================================ -- Modified by: DoctorX (2016) (removed all object references from printf( ) function calls) -- Modified by: Tronex (2018-2019) (updated and optimized scripts, codes from CoC 1.5) -- ============================================================ local sign_add = "+" -- 1 local sign_sub = "-" -- 2 local sign_equ = "=" -- 3 local sign_neg = "!" -- 4 local sign_prb = "~" -- 5 -- spawn items used by xr_logic.configure_schemes local function create_items(npc, section, number) local sim = alife() for i=1,number do alife_create_item(section, npc) end end function spawn_items(npc, st) --utils_data.debug_write("spawner:spawn_items") if not (st.ini and st.section_logic) then return end local items_to_spawn = {} local spawn_items_section = st.ini:r_string_ex(st.section_logic,"spawn") if spawn_items_section ~= nil then local n = st.ini:line_count(spawn_items_section) local id, value = "", "" for i=0,n-1 do result, id, value = st.ini:r_line(spawn_items_section,i,"","") if value == "" then value = 1 end items_to_spawn[id] = tonumber(value) end local inv = {} local function add(item) inv[item:id()] = item end npc:inventory_for_each(add) for k,v in pairs(items_to_spawn) do if npc:object(k) == nil then create_items(npc, k, v) end end end end function configure_schemes(npc, ini, ini_filename, stype, section_logic, gulag_name) --printf("DEBUG: enable_scripts: npc:name()=%s", npc:name()) if not (npc) then abort("configure_schemes: NPC IS NIL!") return end local npc_id = npc:id() local st = db.storage[npc_id] if not (st) then return end -- if some scheme was previously active, deactivate it if st.active_section then issue_event(npc, st[st.active_scheme], "deactivate", npc) end local actual_ini local actual_ini_filename if (ini_filename == "") then -- the case where custom_data in object section exists local custom_data = ini_sys:r_string_ex(npc:section(),"custom_data") if (custom_data) then actual_ini_filename = custom_data actual_ini = ini_file(custom_data) end end local filename = ini:r_string_ex(section_logic,"cfg") if filename then -- a link, because custom_data cannot have more then 4096 bytes of data actual_ini_filename = filename actual_ini = ini_file(filename) end actual_ini_filename = actual_ini_filename or ini_filename actual_ini = actual_ini or ini if not (actual_ini) then callstack() printe("!ERROR: object '%s' actual_ini=%s actual_ini_filename=%s %s=%s", npc:name(),actual_ini ~= nil,actual_ini_filename,section_logic,actual_ini and actual_ini:section_exist(section_logic)) return end -- Since at the moment of schema activation the previously installed common schemes could work, all of them should be disabled: disable_generic_schemes(npc, stype) -- We include all the general schemes (wounded, callback for hit, etc.): enable_generic_schemes(actual_ini, npc, stype, section_logic) st.active_section = nil st.active_scheme = nil st.gulag_name = gulag_name st.stype = stype st.ini = actual_ini st.ini_filename = actual_ini_filename st.section_logic = section_logic -- Инициализация торговли if (stype == modules.stype_trader or stype == modules.stype_stalker) then if (npc:alive()) then local trade_ini = actual_ini:r_string_ex(section_logic,"trade") or "items\\trade\\trade_generic.ltx" trade_manager.trade_init(npc, trade_ini) spawn_items(npc, st) end end return st.ini end -- Вызывается биндером с целью определить первую активную схему function determine_section_to_activate(npc, ini, section_logic, actor) if not (ini and ini:section_exist(section_logic)) then return "nil" end if db.offline_objects[npc:id()] and db.offline_objects[npc:id()].active_section ~= nil then local sect_to_retr = db.offline_objects[npc:id()].active_section db.offline_objects[npc:id()].active_section = nil if (ini:section_exist(sect_to_retr)) then return sect_to_retr end end -- Распарсить строку выбора активной секции с учетом команд, заключенных в %% local active_section_cond = cfg_get_condlist(ini, section_logic, "active", npc) if not (active_section_cond) then return "nil" end local active_section = pick_section_from_condlist(actor, npc, active_section_cond.condlist) if not active_section then printf("object '%s': section '%s': section 'active' has no conditionless else clause",npc:name(), section_logic) return "nil" end return active_section end ------------------------------------------------------------------------------------------------------------ -- ВНОСЯ ИЗМЕНЕНИЯ В ЭТУ ФУНКЦИЮ, НЕ ЗАБЫВАЙТЕ ДОБАВЛЯТЬ СООТВЕТСТВУЮЩИЕ СТРОКИ И В enable_generic_schemes ------------------------------------------------------------------------------------------------------------ function disable_generic_schemes(npc, stype) modules.disable_generic_schemes(npc,stype) end ------------------------------------------------------------------------------------------------------------ -- ВНОСЯ ИЗМЕНЕНИЯ В ЭТУ ФУНКЦИЮ, НЕ ЗАБЫВАЙТЕ ДОБАВЛЯТЬ СООТВЕТСТВУЮЩИЕ СТРОКИ И В disable_generic_schemes ------------------------------------------------------------------------------------------------------------ function enable_generic_schemes(ini, npc, stype, section) modules.enable_generic_schemes(npc,ini,section,stype) end -- Существует два способа вызова функции: с передаванием секции или без нее -- В обоих случаях передавать gulag_name ОБЯЗАТЕЛЬНО -- Если section не передана, то она берется из работы переданного гулага. function activate_by_section(npc, ini, section, gulag_name, loading) --printf("LOGIC[%s]: Object '%s': activate_by_section: looking for section '%s'", tostring(time_global()), npc:name(), section) if not (ini) then callstack() printf("activate_by_section: INI is nil! npc=%s section=%s",npc and npc:name(),section) return end if loading == nil then callstack() printe("!ERROR: xr_logic: activate_by_section: loading field is nil, true or false expected") end local npc_id = npc:id() if not (loading) then db.storage[npc_id].activation_time = time_global() db.storage[npc_id].activation_game_time = game.get_game_time() else local tg = time_global() local act_time = db.storage[npc_id].activation_time or tg db.storage[npc_id].activation_time = tg >= act_time and act_time or tg -- activation_time can never be larger then tg, if so it's an error, correct! db.storage[npc_id].activation_game_time = db.storage[npc_id].activation_game_time or game.get_game_time() end if section == "nil" then db.storage[npc_id].overrides = nil reset_generic_schemes_on_scheme_switch(npc, "nil", "nil") db.storage[npc_id].active_section = nil db.storage[npc_id].active_scheme = nil --' db.storage[npc_id].pstor = nil return end if section == nil then local current_gulag = xr_gulag.get_npc_smart(npc) if current_gulag == nil then printe("!ERROR: xr_logic: activate_by_section: section is NIL and NPC not in gulag.") end local t = current_gulag and current_gulag:getJob(npc_id) if not (t) then db.storage[npc_id].overrides = nil reset_generic_schemes_on_scheme_switch(npc, "nil", "nil") db.storage[npc_id].active_section = nil db.storage[npc_id].active_scheme = nil return end section = t.section end if not ini:section_exist(section) then printf("object '%s': activate_by_section: section '%s' does not exist", npc:name(), section) end local scheme = utils_data.get_scheme_by_section(section) if scheme == nil then printf("object '%s': unable to determine scheme name from section name '%s'", npc:name(), section) end if not (db.storage[npc_id].stype and schemes_by_stype[db.storage[npc_id].stype][scheme]) then printe("!ERROR: %s trying to use a scheme not intended for stype scheme=%s stype=%s",npc:name(),scheme,db.storage[npc_id].stype) return end -- Загрузить оверрайды: db.storage[npc_id].overrides = cfg_get_overrides(ini, section, npc, db.storage[npc_id].overrides) -- Сбросить общие схемы: reset_generic_schemes_on_scheme_switch(npc, scheme, section) -- schemes[scheme] даст имя файла (модуля), в котором реализована схема -- _G[] даст указатель на неймспейс (таблицу) этого модуля local filename = schemes[scheme] if filename == nil then printe("!ERROR: xr_logic: scheme '%s' is not registered in modules.script", scheme) end --printf("_bp: calling module('%s')", filename) if (not _G[filename] or not _G[filename].set_scheme) then printe("!ERROR: xr_logic: can't call %s.set_scheme() - a nil value", filename) return end _G[filename].set_scheme(npc, ini, scheme, section, gulag_name) --printf("DEBUG: activate_by_section: scheme '%s' activated from section '%s'", scheme, section) db.storage[npc_id].active_section = section db.storage[npc_id].active_scheme = scheme if db.storage[npc_id].stype == modules.stype_stalker then -- чтобы избежать дальнейшего движения по пути при установке рестрикторов npc:inactualize_patrol_path() -- Alundaio: Necessary to clear path data or npc can get stuck on generic schemes npc:set_path_type(game_object.level_path) utils_obj.send_to_nearest_accessible_vertex(npc, npc:level_vertex_id(),"xr_logic") issue_event(npc, db.storage[npc_id][scheme], "activate_scheme", loading, npc) else issue_event(npc, db.storage[npc_id][scheme], "reset_scheme", loading, npc) end end --[[ -- Предназначение: -- Производит сброс состояния объекта (снимает коллбеки, отключает разговор) непосредственно перед включением -- новой схемы. ]] -- function reset_generic_schemes_on_scheme_switch(npc, scheme, section) modules.reset_generic_schemes(npc,scheme,section) end function assign_storage_and_bind(npc,ini,scheme,section,temp) local id = npc:id() if not (db.storage[id][scheme]) then db.storage[id][scheme] = {} db.storage[id][scheme].npc = npc if not (_G[schemes[scheme]] and _G[schemes[scheme]].add_to_binder) then callstack() printe("!ERROR: no scheme found in schemes table or scheme has no add_to_binder function! scheme = %s",scheme) end if (temp) then temp.needs_configured = true end --printf("DEBUG: add_to_binder npc=%s scheme=%s section=%s",npc and npc:name(),scheme,section) --utils_data.debug_write(strformat("BEFORE: add_to_binder npc=%s scheme=%s section=%s",npc and npc:name(),scheme,section)) _G[schemes[scheme]].add_to_binder(npc, ini, scheme, section, db.storage[id][scheme], temp) --utils_data.debug_write(strformat("AFTER: add_to_binder npc=%s scheme=%s section=%s",npc and npc:name(),scheme,section)) end --db.storage[id][scheme].scheme = scheme db.storage[id][scheme].section = section db.storage[id][scheme].ini = ini return db.storage[id][scheme] end function subscribe_action_for_events(npc, storage, new_action) --printf("DEBUG: registering new action for reset event scheme=%s npc=%s",scheme, npc and npc:name()) if not (storage) then callstack() printe("!ERROR: subscribe_action_for_events storage is nil!") end if not storage.actions then storage.actions = {} end storage.actions[new_action] = true end function unsubscribe_action_from_events(npc, storage, new_action) if not storage.actions then storage.actions = {} end storage.actions[new_action] = nil end -- st - storage активной схемы function issue_event(npc, st, event_fn, ...) if not st or not st.actions then return end local activation_count = 0 local action_ptr, is_active = 0, 0 for action_ptr, is_active in pairs(st.actions) do if is_active and action_ptr[event_fn] then action_ptr[event_fn](action_ptr, ...) activation_count = activation_count + 1 end end -- if activation_count == 0 and -- event_fn == "activate_scheme" -- then -- printf("xr_logic: issue_event: activate_scheme handler not found, active_scheme is '%s'", db.storage[npc:id()].active_scheme) -- end end --' Функция проверяет выполняется ли у активной схемы заданная функция function check_action(npc, st, event_fn, p) if not st or not st.actions then return true end for action_ptr, is_active in pairs(st.actions) do if is_active and action_ptr[event_fn] then return action_ptr[event_fn](action_ptr, p) end end return true end function pick_section_from_condlist(obj, npc, condlist) -- New if not (condlist) then return "" end local npc_id = npc and (npc.id and type(npc.id) == "function" and npc:id() or npc.id) npc_id = npc_id == AC_ID and nil or npc_id -- we want only npc not actor local actor = db.actor local sim = alife() for i=1,#condlist do local cond = condlist[i] local infop_conditions_met = true if (cond[2]) then for j=1,#cond[2] do local infop = cond[2][j] if (infop[2] == 1) then if (infop[3]) then if (npc_id) then if not (npc:has_info(infop[1])) then infop_conditions_met = false break end end else if (actor) then if not (actor:has_info(infop[1])) then infop_conditions_met = false break end else if not (sim:has_info(0,infop[1])) then infop_conditions_met = false break end end end elseif (infop[2] == 2) then if (infop[3]) then if (npc_id) then if (npc:has_info(infop[1])) then infop_conditions_met = false break end end else if (actor) then if (actor:has_info(infop[1])) then infop_conditions_met = false break end else if (sim:has_info(0,infop[1])) then infop_conditions_met = false break end end end elseif (infop[2] == 3) then if not (xr_conditions[infop[1]]) then printf("pick_section_from_condlist xr_conditions fail! obj=%s functor=%s",npc and npc:name(),infop[1]) elseif not xr_conditions[infop[1]](obj, npc, infop[3]) then infop_conditions_met = false break end elseif (infop[2] == 4) then if not (xr_conditions[infop[1]]) then printf("pick_section_from_condlist xr_conditions fail! obj=%s functor=%s",npc and npc:name(),infop[1]) elseif xr_conditions[infop[1]](obj, npc, infop[3]) then infop_conditions_met = false break end elseif (infop[2] == 5) then if (infop[3] and infop[3] < math.random(100)) then infop_conditions_met = false break end end end end if (infop_conditions_met) then if (cond[3]) then for j=1,#cond[3] do local infop = cond[3][j] if (infop[2] == 1) then if (infop[3]) then if (npc_id) then if not (npc:has_info(infop[1])) then npc:give_info_portion(infop[1]) end end else if (actor) then if not (actor:has_info(infop[1])) then actor:give_info_portion(infop[1]) end else if not (sim:has_info(0,infop[1])) then sim:give_info(0,infop[1]) end end end elseif (infop[2] == 2) then if (infop[3]) then if (npc_id) then if (npc:has_info(infop[1])) then npc:disable_info_portion(infop[1]) end end else if (actor) then if (actor:has_info(infop[1])) then actor:disable_info_portion(infop[1]) end else if (sim:has_info(0,infop[1])) then sim:disable_info(0,infop[1]) end end end elseif (infop[2] == 3 or infop[2] == 4) then if not (xr_effects[infop[1]]) then printf("pick_section_from_condlist xr_effects fail! obj=%s functor=%s",npc and npc:name(),infop[1]) end xr_effects[infop[1]](obj, npc, infop[3]) end end end return cond[1] or "" end end return end -- Выполняет переключение на указанную секцию, если задана. -- Если section == nil, остается работать старая секция. function switch_to_section(npc, ini, section) if section == "" or section == nil then return false end local npc_id = npc:id() local active_section = db.storage[npc_id].active_section if active_section == section then return false end db.storage[npc_id].overrides = cfg_get_overrides(ini, section, npc, db.storage[npc_id].overrides) if (axr_beh) then save_var(npc,"path_index",1) end if active_section then local st = db.storage[npc_id][db.storage[npc_id].active_scheme] issue_event(npc, st, "deactivate", npc, st) end db.storage[npc_id].exit_from_smartcover_initialized = nil db.storage[npc_id].active_section = nil db.storage[npc_id].active_scheme = nil activate_by_section(npc, ini, section, db.storage[npc_id].gulag_name, false) return true end function see_actor(npc) return npc:alive() and npc:see(db.actor) end function try_switch_to_another_section(npc, st, actor) if not actor then --printf("try_switch_to_another_section(): error in implementation of scheme '%s': actor is nil", st.scheme) return end local l = st.logic local npc_id = npc:id() if not l then --printf("DEBUG: %s Can't find script switching information in storage, scheme=%s ini=%s",npc:name(),db.storage[npc_id].active_scheme,db.storage[npc_id].job_ini or db.storage[npc_id].ini_filename) return end local switched = false for n, c in pairs(l) do if (c.name == "on_actor_dist_le") then if see_actor(npc) and distance_between(actor, npc) <= c.v1 then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_dist_le_nvis") then if distance_between(actor, npc) <= c.v1 then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_dist_ge") then if see_actor(npc) and distance_between(actor, npc) > c.v1 then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_dist_ge_nvis") then if distance_between(actor, npc) > c.v1 then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_signal") then if st.signals and st.signals[c.v1] then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_info") then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) elseif (c.name == "on_timer") then if time_global() >= db.storage[npc_id].activation_time + c.v1 then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_game_timer") then if game.get_game_time():diffSec(db.storage[npc_id].activation_game_time) >= c.v1 then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_in_zone") then if (db.actor_inside_zones[c.v1]) then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_not_in_zone") then if not (db.actor_inside_zones[c.v1]) then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_npc_in_zone") then if utils_obj.npc_in_zone(level.object_by_id(c.npc_id), db.zone_by_name[c.v2]) then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_npc_not_in_zone") then if not utils_obj.npc_in_zone(level.object_by_id(c.npc_id), db.zone_by_name[c.v2]) then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_inside") then if utils_obj.npc_in_zone(actor, npc) then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end elseif (c.name == "on_actor_outside") then if not utils_obj.npc_in_zone(actor, npc) then switched = switch_to_section(npc, st.ini, pick_section_from_condlist(actor, npc, c.condlist)) end else printf("WARNING: object '%s': try_switch_to_another_section: unknown condition '%s' encountered",npc:name(), c.name) end if switched == true then break end end return switched end function is_active(npc, st) if st.section == nil then callstack() printf("npc '%s': st.section is nil, active section is '%s'",npc:name(), utils_data.to_str(db.storage[npc:id()].active_section)) return false end -- Текущая секция активна и не сработало ни одно из условий переключения на другие секции return st.section == db.storage[npc:id()].active_section end function abort_syntax_error_in_cond(section,field,c) callstack() abort("section '%s': field '%s': syntax error in switch condition [%s]",section,field,c) end -- На входе имеем пустой массив и строку вида " +infop1 -infop2 +infop3 ... " -- Заполнить массив: -- { "infop_name" = true/false }. function parse_infop1(rslt, str) --printf("_bp: parse_infop: %s", utils_data.to_str(str)) if str then local infop_name, sign local infop_n = 1 for s in string.gmatch(str, "%s*([%-%+%~%=%!][^%-%+%~%=%!%s]+)%s*") do --printf("_bp: parse_infop: s=%s", utils_data.to_str(s)) sign = string.sub(s, 1, 1) infop_name = string.sub(s, 2) if sign == "+" then rslt[infop_n] = { name = infop_name, required = true } elseif sign == "-" then rslt[infop_n] = { name = infop_name, required = false } elseif sign == "~" then rslt[infop_n] = { prob = tonumber(infop_name) } elseif sign == "=" then rslt[infop_n] = { func = infop_name, expected = true } elseif sign == "!" then rslt[infop_n] = { func = infop_name, expected = false } else abort_syntax_error_in_cond(npc, section, field) end infop_n = infop_n + 1 end end end function parse_func_params(str) local lst = {} local n for par in string.gmatch(str, "%s*([^:]+)%s*") do n = tonumber(par) if n then lst[#lst+1] = n -->> might be an error else lst[#lst+1] = par end end return lst end function parse_infop(rslt, str) --printf("_bp: parse_infop1: %s", utils_data.to_str(str)) if str then local infop_name, sign local infop_n = 1 local at, params for s in string.gmatch(str, "%s*([%-%+%~%=%!][^%-%+%~%=%!%s]+)%s*") do --printf("_bp: parse_infop: s=%s", utils_data.to_str(s)) sign = string.sub(s, 1, 1) infop_name = string.sub(s, 2) params = nil -- парсим параметры функций at = string.find(infop_name, "%(") if at then if string.sub(infop_name, -1) ~= ")" then -- printf("wrong condlist %s", str) printf( "wrong condlist" ) end if at < string.len(infop_name) - 1 then params = parse_func_params(string.sub(infop_name, at + 1, -2)) else params = {} end infop_name = string.sub(infop_name, 1, at - 1) end if sign == "+" then rslt[infop_n] = { name = infop_name, required = true } elseif sign == "-" then rslt[infop_n] = { name = infop_name, required = false } elseif sign == "~" then rslt[infop_n] = { prob = tonumber(infop_name) } elseif sign == "=" then --printf("_bp: n = %s; r = %s", infop_name, utils_data.to_str(params)) rslt[infop_n] = { func = infop_name, expected = true, params = params } elseif sign == "!" then --printf("_bp: n = %s; r = %s", infop_name, utils_data.to_str(params)) rslt[infop_n] = { func = infop_name, expected = false, params = params } else abort_syntax_error_in_cond(npc, section, field) end infop_n = infop_n + 1 end end end -- Распарсивает строку src вида: -- {+infop1} section1 %-infop2%, {+infop3 -infop4} section2 ... -- в таблицу: -- { -- 1 = { infop_check = { 1 = {"infop1" = true} }, infop_set = { 1 = {"infop2" = false } }, section = "section1" }, -- 2 = { infop_check = { 1 = {"infop3" = true}, 2 = {"infop4" = false} }, infop_set = {}, section = "section2" }, -- } function extract_conditions(c,lst,n,index) -- New for sign,infop in c:gmatch("([=%-%+!~]+)([^=%-%+!~%s]+)") do if not (lst[n][index]) then lst[n][index] = {} -- because we don't want empty tables end if (sign == sign_add) then table.insert(lst[n][index],{infop,1,(string.find(infop,"npcx_"))}) elseif (sign == sign_sub) then table.insert(lst[n][index],{infop,2,(string.find(infop,"npcx_"))}) elseif (sign == sign_equ or sign == sign_neg) then a = string.find(infop,"(",1,true) if (a) then b = string.find(infop,")",1,true) if (b) then local params = infop:sub(a+1,b-1) infop = infop:sub(1,a-1) --.. infop:sub(b+1) local t for p in params:gmatch("%s*([^:]+)%s*") do if not (t) then t = {} end table.insert(t,tonumber(p) or p) end table.insert(lst[n][index],{infop,sign == sign_equ and 3 or 4,t}) else abort_syntax_error_in_cond(section,field,c) end else table.insert(lst[n][index],{infop,sign == sign_equ and 3 or 4}) end elseif (sign == sign_prb) then table.insert(lst[n][index],{infop,5,tonumber(infop)}) else abort_syntax_error_in_cond(section,field,c) end end end local memoize_condlist = {} -- Because there are many condlist that are identical, they will have identical results function parse_condlist(npc, section, field, src) -- New --if (src == nil or type(src) == "boolean") then if not (src and src ~= "") then callstack() printe("!ERROR!! xr_logic.parse_condlist npc=%s section=%s field=%s src=%s",npc and npc:name(),section,field,src) return end if (memoize_condlist[src]) then --printf("%s (%s:%s) | %s",npc and npc:name(),section,field,src) return memoize_condlist[src] end local lst = {} local n = 0 for fld in src:gmatch("%s*([^,]+)%s*") do local a,b n = n + 1 lst[n] = {} local a,b,c = string.find(fld, "{%s*(.*)%s*}") if (c) then fld = string.sub(fld,1,a-1) .. string.sub(fld,b+1) extract_conditions(c,lst,n,2) end a,b,c = string.find(fld, "%%%s*(.*)%s*%%") if (c) then fld = string.sub(fld,1,a-1) .. string.sub(fld,b+1) extract_conditions(c,lst,n,3) end fld = fld:trim() lst[n][1] = fld end memoize_condlist[src] = lst return lst end function cfg_get_number_and_condlist(ini, section, field, npc,cond_name, str) str = str or ini:r_string_ex(section,field) if not str then return nil end local par = utils_data.parse_params(str) if not par[1] or not par[2] then abort_syntax_error_in_cond(section,field,str) end local t = {} t.name = cond_name or field t.v1 = tonumber(par[1]) t.condlist = parse_condlist(npc, section, field, par[2]) return t end function cfg_get_string_and_condlist(ini, section, field, npc, cond_name, str) str = str or ini:r_string_ex(section,field) if not str then return nil end local par = utils_data.parse_params(str) if not par[1] or not par[2] then abort_syntax_error_in_cond(section,field,str) end local t = {} t.name = cond_name or field t.v1 = par[1] t.condlist = parse_condlist(npc, section, field, par[2]) return t end function cfg_get_two_strings_and_condlist(ini, section, field, npc, cond_name, str) str = str or ini:r_string_ex(section,field) if not str then return nil end local par = utils_data.parse_params(str) if not par[1] or not par[2] or not par[3] then abort_syntax_error_in_cond(section,field,str) end local t = {} t.name = cond_name or field t.v1 = par[1] t.v2 = par[2] t.condlist = parse_condlist(npc, section, field, par[3]) return t end function cfg_get_condlist(ini, section, field, npc, cond_name, str) local str = ini:r_string_ex(section,field) if not str then --printf("DEBUG: cfg_get_condlist: section=%s field=%s is nil",section,field) return nil end local par = utils_data.parse_params(str) if not par[1] then abort_syntax_error_in_cond(section,field,str) end local t = {} t.name = cond_name or field t.condlist = parse_condlist(npc, section, field, par[1]) return t end function cfg_get_npc_and_zone(ini, section, field, npc,cond_name) local t = cfg_get_two_strings_and_condlist(ini, section, field, npc, cond_name) if t then local id = get_story_object_id(t.v1) if not (id) then printf("object '%s': section '%s': field '%s': there is no object with story_id '%s'",npc:name(), section, field, t.v1) t.npc_id = 65535 end local sim = alife() local se_obj = sim:object(id) t.npc_id = se_obj ~= nil and se_obj.id or 65535 end return t end function cfg_get_switch_conditions(ini, section, npc) if not (section and ini:section_exist(section)) then --callstack() --printf("DEBUG: cfg get switch cond: [%s] section %s does not exist! ini=%s",npc and npc:name(),section,db.storage[npc:id()].ini_filename) return {} end local lst = {} local line_count = ini:line_count(section) for line_number = 0, line_count - 1 do local result, id, value = ini:r_line_ex(section,line_number,"","") local cond_name = id:gsub("%d*$","") if (cond_name == "on_actor_dist_le") then lst[#lst+1] = cfg_get_number_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_dist_le_nvis") then lst[#lst+1] = cfg_get_number_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_dist_ge") then lst[#lst+1] = cfg_get_number_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_dist_ge_nvis") then lst[#lst+1] = cfg_get_number_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_signal") then lst[#lst+1] = cfg_get_string_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_first_update") then lst[#lst+1] = cfg_get_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_info") then lst[#lst+1] = cfg_get_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_timer") then lst[#lst+1] = cfg_get_number_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_game_timer") then lst[#lst+1] = cfg_get_number_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_in_zone") then lst[#lst+1] = cfg_get_string_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_not_in_zone") then lst[#lst+1] = cfg_get_string_and_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_inside") then lst[#lst+1] = cfg_get_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_actor_outside") then lst[#lst+1] = cfg_get_condlist(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_npc_in_zone") then lst[#lst+1] = cfg_get_npc_and_zone(ini,section,id,npc,cond_name,value) elseif (cond_name == "on_npc_not_in_zone") then lst[#lst+1] = cfg_get_npc_and_zone(ini,section,id,npc,cond_name,value) end end return lst end function cfg_get_overrides(ini, section, npc, l) l = empty_table(l) -- reuse to not create GC waste l.combat_ignore = cfg_get_condlist(ini, section, "combat_ignore_cond", npc) l.enemy_ignore = cfg_get_condlist(ini, section, "enemy_ignore_cond", npc) l.no_combat_job = cfg_get_condlist(ini, section, "no_combat_job", npc) l.combat_ignore_keep_when_attacked = ini:r_bool_ex(section, "combat_ignore_keep_when_attacked",false) l.combat_type = cfg_get_condlist(ini, section, "combat_type", npc) l.on_combat = cfg_get_condlist(ini, section, "on_combat", npc) local st = db.storage[npc:id()] if ini:line_exist(st.section_logic, "post_combat_time") then l.min_post_combat_time,l.max_post_combat_time = ini:r_mult(st.section_logic,"post_combat_time",10,15) else l.min_post_combat_time,l.max_post_combat_time = ini:r_mult(section,"post_combat_time",10,15) end l.min_post_combat_time,l.max_post_combat_time = tonumber(l.min_post_combat_time),tonumber(l.max_post_combat_time) if ini:line_exist(section, "on_offline") then l.on_offline_condlist = xr_logic.parse_condlist(npc, section, "on_offline", ini:r_string_ex(section,"on_offline") or "nil") else l.on_offline_condlist = xr_logic.parse_condlist(npc, st.section_logic, "on_offline", ini:r_string_ex(st.section_logic,"on_offline") or "nil") end if string.find(section, "kamp") ~= nil then l.soundgroup = ini:r_string_ex(section,"center_point") else l.soundgroup = ini:r_string_ex(section,"soundgroup") end return l end -- Возвращает ссылку на оверрайды, зарегистрированные в активной на данный момент секции, -- либо nil, если ни одна из секций не активна, или оверрайдов нет. function generic_scheme_overrides(npc) return db.storage[npc:id()].overrides end function mob_release(mob) if mob:get_script() then mob:script(false, script_name()) end end function mob_capture(mob, reset_actions) if reset_actions then reset_action(mob, script_name()) else printf("mob_capture: reset_actions parameter's value is not specified") if not mob:get_script() then mob:script(true, script_name()) end end end function mob_captured(mob) return mob:get_script() end local pstor_number = 0 local pstor_string = 1 local pstor_boolean = 2 function pstor_save_all(obj, packet) local npc_id = obj:id() if (USE_MARSHAL) then db.storage[npc_id].pstor = db.storage[npc_id].pstor or {} SendScriptCallback("on_pstor_save_all",obj,packet) --printf("pstor_save_all obj %s after",obj and obj:name()) return end local pstor = db.storage[npc_id].pstor if not pstor then pstor = {} db.storage[npc_id].pstor = pstor end local ctr = 0 for k, v in pairs(pstor) do ctr = ctr + 1 end packet:w_u32(ctr) for k, v in pairs(pstor) do --printf("pstor_save_all:%s: %s=%s",obj:name(),k,v) utils_data.w_stpk(packet,"stringZ",k,obj:name()..":pstor_save_all") local tv = type(v) if tv == "number" then packet:w_u8(pstor_number) utils_data.w_stpk(packet,"float",v,obj:name()..":pstor_save_all") elseif tv == "string" then packet:w_u8(pstor_string) utils_data.w_stpk(packet,"stringZ",v,obj:name()..":pstor_save_all") elseif tv == "boolean" then packet:w_u8(pstor_boolean) utils_data.w_stpk(packet,"bool",v,obj:name()..":pstor_save_all") else printf("xr_logic: pstor_save_all: not registered type '%s' encountered", tv) end end pstor = db.storage[npc_id].pstor_ctime if not pstor then pstor = {} db.storage[npc_id].pstor_ctime = pstor end ctr = 0 for k, v in pairs(pstor) do ctr = ctr + 1 end packet:w_u32(ctr) for k,v in pairs(pstor) do utils_data.w_stpk(packet,"stringZ",k,obj:name()..":pstor_save_all") utils_data.w_stpk(packet,"CTime",v,obj:name()..":pstor_save_all:GAMETIME") end end function pstor_load_all(obj, reader) local npc_id = obj:id() if (USE_MARSHAL) then db.storage[npc_id].pstor = db.storage[npc_id].pstor or {} SendScriptCallback("on_pstor_load_all",obj,reader) return end db.storage[npc_id].pstor = db.storage[npc_id].pstor or {} if (reader:r_elapsed() <= 0) then return end local ctr = reader:r_u32() for i = 1, ctr do if (reader:r_elapsed() <= 0) then return end local varname = reader:r_stringZ() local tn = reader:r_u8() if tn == pstor_number then db.storage[npc_id].pstor[varname] = reader:r_float() elseif tn == pstor_string then db.storage[npc_id].pstor[varname] = reader:r_stringZ() elseif tn == pstor_boolean then db.storage[npc_id].pstor[varname] = reader:r_bool() else printf("xr_logic: pstor_load_all: not registered type '%s' encountered %s", tn,obj:name()) end --printf("_bp: pstor_load_all: loaded [%s]='%s'", varname, utils_data.to_str(db.storage[npc_id].pstor[varname])) end db.storage[npc_id].pstor_ctime = db.storage[npc_id].pstor_ctime or {} ctr = reader:r_u32() for i = 1, ctr do if (reader:r_elapsed() <= 0) then return end local varname = reader:r_stringZ() db.storage[npc_id].pstor_ctime[varname] = utils_data.r_CTime(reader) end end function save_obj(obj, packet) if not (obj) then callstack() error("xr_logic:save_obj obj is nil!") return end set_save_marker(packet, "save", false, "object "..obj:name()) local npc_id = obj:id() local st = db.storage[npc_id] if not (st) then printf("save_obj WARNING: db.storage is nil! %s",obj:name()) return end --packet:w_stringZ(st.job_ini or "") packet:w_stringZ(st.ini_filename or "") packet:w_stringZ(st.section_logic or "") packet:w_stringZ(st.active_section or "") packet:w_stringZ(st.gulag_name or "") local activation_time = db.storage[npc_id].activation_time or 0 utils_data.w_stpk(packet,"s32",activation_time - time_global(),obj:name()..":save_logic:activation_time - cur_tm") utils_data.w_stpk(packet,"CTime",db.storage[npc_id].activation_game_time,obj:name()..":save_logic:GAMETIME") if st.active_scheme then issue_event(obj, db.storage[npc_id][st.active_scheme], "save") end pstor_save_all(obj, packet) set_save_marker(packet, "save", true, "object "..obj:name()) end function load_obj(obj, reader) if not (obj) then callstack() error("xr_logic:load_obj obj is nil!") return end set_save_marker(reader, "load", false, "object "..obj:name()) local npc_id = obj:id() local st = db.storage[npc_id] if not (st) then error(strformat("load_obj st is nil for %s",obj and obj:name())) end --[[ local job_ini = reader:r_stringZ() if job_ini == "" then job_ini = nil end --]] local ini_filename = reader:r_stringZ() if ini_filename == "" then ini_filename = nil end local section_logic = reader:r_stringZ() if section_logic == "" then section_logic = nil end local active_section = reader:r_stringZ() if active_section == "" then active_section = "nil" end local gulag_name = reader:r_stringZ() if (gulag_name == "nil") then gulag_name = "" end --st.job_ini = job_ini st.loaded_ini_filename = ini_filename st.loaded_section_logic = section_logic st.loaded_active_section = active_section st.loaded_gulag_name = gulag_name db.storage[npc_id].activation_time = reader:r_s32() + time_global() db.storage[npc_id].activation_game_time = utils_data.r_CTime(reader) or game.get_game_time() pstor_load_all(obj, reader) set_save_marker(reader, "load", true, "object "..obj:name()) end function get_customdata_or_ini_file(npc, filename) if filename == "" then local ini = npc:spawn_ini() if (ini) then local link_filename = ini:r_string_ex("logic","cfg") if (link_filename) then if (getFS():exist("$game_config$",link_filename)) then return ini_file(link_filename), link_filename else printf("1:get_customdata_or_ini_file %s file %s doesn't exist!",npc:name(),link_filename) end end return ini, filename end return ini_file("default_custom_data.ltx"), "default_custom_data.ltx" elseif string.find( filename, "*" ) == 1 then local gname = string.sub(filename, 2) return db.dynamic_ltx[gname], filename else -- dynamic ltx found and already loaded if (db.dynamic_ltx[filename]) then return db.dynamic_ltx[filename], filename end if (getFS():exist("$game_config$",filename)) then return ini_file(filename), filename else printf("2:get_customdata_or_ini_file %s file %s doesn't exist!",npc:name(),filename) end end return ini_file("default_custom_data.ltx"), "default_custom_data.ltx" end function initialize_obj(obj, st, loaded, actor, stype) if (IsStalker(obj)) then if (obj:has_info("npcx_is_companion")) then axr_companions.setup_companion_logic(obj,st,loaded) return end end local ini, ini_filename = get_customdata_or_ini_file(obj, loaded and st.loaded_ini_filename or "") ini = configure_schemes(obj, ini, ini_filename, stype, loaded and st.loaded_section_logic or "logic", loaded and st.loaded_gulag_name or "") local section = loaded and st.loaded_active_section or determine_section_to_activate(obj, ini, "logic", actor) activate_by_section(obj, ini, section, loaded and st.loaded_gulag_name or "", true) -- no need to keep this information after initializing st.loaded_ini_filename = nil st.loaded_active_section = nil st.loaded_section_logic = nil st.loaded_gulag_name = "" end -------------------------------------------------------------- --axr_logic function set_new_scheme_and_logic(npc,scheme,section,logic,gulag,ini_filename) if not (npc) then printf("set_new_scheme_and_logic npc is nil!") end local st = db.storage[npc:id()] --save_var(npc,"old_active_section", st.active_section) --save_var(npc,"old_section_logic", st.section_logic) --save_var(npc,"old_ini_filename", st.ini_filename) -- Store new ini db.storage[npc:id()].ini_filename = ini_filename db.storage[npc:id()].ini = ini_file(ini_filename) if not (st.ini) then printe("!ERROR: set_new_scheme_and_logic: cannot find ini %s",ini_filename) return end -- Set new section logic db.storage[npc:id()].section_logic = logic -- Select new active section local new_section = section or determine_section_to_activate(npc, st.ini, st.section_logic) db.storage[npc:id()].overrides = cfg_get_overrides(st.ini, new_section, npc, db.storage[npc:id()].overrides) -- Switch to new section switch_to_section(npc, st.ini, new_section) end function restore_scheme_and_logic(npc) local st = db.storage[npc:id()] st.ini, st.ini_filename = get_customdata_or_ini_file(npc, "") st.active_section = nil st.active_scheme = nil st.section_logic = "logic" switch_to_section(npc,nil,nil) end function reset_logic(npc,st) st.ini, st.ini_filename = get_customdata_or_ini_file(npc, "") st.active_section = nil st.active_scheme = nil st.section_logic = "logic" switch_to_section(npc,nil,nil) end function str_from_condlist(obj,ini,section,field) local str = section and ini and ini:section_exist(section) and ini:line_exist(section,field) and ini:r_string_ex(section,field) or "nil" local condlist = parse_condlist(obj,section,field,str) return pick_section_from_condlist(db.actor,obj,condlist) end